﻿using HDSmartCommunity.BUS;
using HDSmartCommunity.BUS.MqttBUS;
using HDSmartCommunity.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using Timer = System.Timers.Timer;

namespace HDSmartCommunity.DevicePlatform
{
    public class PZDeviceSerialData
    {
        public SerialPort myS0SerialPort_1 = new SerialPort();
        public LogPrint logPrint;
        public static CancellationTokenSource cmd_tokenSource = new CancellationTokenSource();
        public CancellationToken cmd_token = cmd_tokenSource.Token;
        public ManualResetEvent cmd_resetEvent = new ManualResetEvent(true);
        public int liftid;
        public int add_start;
        public int add_count;
        public int PZInterval;
        byte[] add_buffer;
        byte[] count_buffer;
        public string numberno;
        public string topic;
        public string remark;
        public PZMqttClientBUS clientBUS;
        public bool reset = false;


        public void OpenS0SerialPort(string portName, int baudRate, int parity, int dataBits, int stopBits)
        {
            try
            {
                SetS0SerialPortConfig(portName, baudRate, parity, dataBits, stopBits);
                myS0SerialPort_1.Open();
                logPrint.Log("串口" + portName + "打开成功");
            }
            catch (Exception e)
            {
                logPrint.Log("串口" + portName + "打开异常"+e.ToString());
            }
        }

        private Timer SerialPortRecvTimerS0_1;
        private static int byteLength = 40960;
        public byte[] byteSerialPortRecvS0_1 = new byte[byteLength];
        public event EventHandler<SerialPort485_1RecvEventArgs> ReceivedDataEventS0_1;
        private static int pSerialPortRecv_S0_1 = 0;

        public class SerialPort485_1RecvEventArgs : EventArgs
        {
            /// <summary>
            /// 广播 收到的串口数据
            /// </summary>
            public readonly byte[] RecvData_485_1 = new byte[byteLength];

            /// <summary>
            /// 收到数据 的 长度
            /// </summary>
            public readonly int RecvDataLength_485_1;

            /// <summary>
            /// 将 收到的数据 转化成 待广播的数据
            /// </summary>
            public SerialPort485_1RecvEventArgs(byte[] recvData, int recvDataLength)
            {
                recvData.CopyTo(RecvData_485_1, 0);
                RecvDataLength_485_1 = recvDataLength;
            }
        }

        private void SetS0SerialPortConfig(string portName, int baudRate, int parity, int dataBits, int stopBits)
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                portName = $"/dev/{portName}";
            }
            // 串口 参数设置
            myS0SerialPort_1.PortName = portName;
            myS0SerialPort_1.BaudRate = baudRate;
            switch (parity)
            {
                case 0:
                default:
                    myS0SerialPort_1.Parity = Parity.None;
                    break;
                case 1:
                    myS0SerialPort_1.Parity = Parity.Odd;
                    break;
                case 2:
                    myS0SerialPort_1.Parity = Parity.Even;
                    break;
                case 3:
                    myS0SerialPort_1.Parity = Parity.Mark;
                    break;
                case 4:
                    myS0SerialPort_1.Parity = Parity.Space;
                    break;
            }
            myS0SerialPort_1.DataBits = ((4 < dataBits) && (dataBits < 9)) ? dataBits : 8;
            switch (stopBits)
            {
                case 0:
                    myS0SerialPort_1.StopBits = StopBits.None;
                    break;
                case 1:
                default:
                    myS0SerialPort_1.StopBits = StopBits.One;
                    break;
                case 2:
                    myS0SerialPort_1.StopBits = StopBits.OnePointFive;
                    break;
                case 3:
                    myS0SerialPort_1.StopBits = StopBits.Two;
                    break;
            }
            //my485SerialPort_1.ReadTimeout = 200;
            myS0SerialPort_1.RtsEnable = true;
            myS0SerialPort_1.DataReceived += new SerialDataReceivedEventHandler(DataReceived_ttyS0);

            // 串口 接收数据处理定时器 参数设置
            SerialPortRecvTimerS0_1 = new System.Timers.Timer();
            SerialPortRecvTimerS0_1.Interval = 100;
            SerialPortRecvTimerS0_1.AutoReset = false;
            SerialPortRecvTimerS0_1.Elapsed += new ElapsedEventHandler(SPRecvTimer_Tick_ttyS0_1);
        }

        private void SPRecvTimer_Tick_ttyS0_1(object sender, EventArgs e)
        {
            byte[] TemporaryData = new byte[byteLength];
            int TemporaryDataLength = 0;

            if (ReceivedDataEventS0_1 != null)
            {
                byteSerialPortRecvS0_1.CopyTo(TemporaryData, 0);
                TemporaryDataLength = pSerialPortRecv_S0_1;

                ReceivedDataEventS0_1.Invoke(this, new SerialPort485_1RecvEventArgs(TemporaryData, TemporaryDataLength));
                // 数据处理完后，将指针指向数据头，等待接收新的数据
                pSerialPortRecv_S0_1 = 0;
            }
        }

        private void DataReceived_ttyS0(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPortRecvTimerS0_1.Stop();
            try
            {
                byte[] ReadBuf = new byte[myS0SerialPort_1.BytesToRead];
                if (pSerialPortRecv_S0_1 > byteLength - ReadBuf.Length)
                {
                    pSerialPortRecv_S0_1 = 0;
                }
                myS0SerialPort_1.Read(ReadBuf, 0, ReadBuf.Length);
                ReadBuf.CopyTo(byteSerialPortRecvS0_1, pSerialPortRecv_S0_1);
                pSerialPortRecv_S0_1 += ReadBuf.Length;
                logPrint.Log("收到数据：" + ToHexs(ReadBuf));
                for (int i = 0; i < byteSerialPortRecvS0_1.Length; i++)
                {
                    if (byteSerialPortRecvS0_1[i] == liftid && byteSerialPortRecvS0_1[i + 1] == 0x03 && byteSerialPortRecvS0_1[i + 2] == add_count * 2)
                    {
                        int data_count = add_count * 2 + 3;
                        byte[] databuffer = new byte[data_count];
                        for (int k = 0; k < data_count; k++)
                        {
                            databuffer[k] = byteSerialPortRecvS0_1[i + k];
                        }

                        byte[] data_crc = Crc18(databuffer);
                        if (data_crc[0] == byteSerialPortRecvS0_1[i + add_count * 2 + 4] && data_crc[1] == byteSerialPortRecvS0_1[i + add_count * 2 + 3])
                        {
                            //databuffer[add_count * 2 + 4] = data_crc[0];
                            //databuffer[add_count * 2 + 5] = data_crc[1];
                            byte[] data = new byte[data_count + 2];
                            for (int m = 0; m < data_count + 2; m++)
                            {
                                data[m] = byteSerialPortRecvS0_1[m + i];
                            }
                            Array.Clear(byteSerialPortRecvS0_1, i, data.Length);
                            if (!string.IsNullOrEmpty(numberno))
                            {
                                PZData pZData = new PZData();
                                pZData.SerialData = ToHexs(data);
                                pZData.SerialNumber = numberno;
                                pZData.Remark = remark;
                                string message = JsonConvert.SerializeObject(pZData);
                                clientBUS.PublicMessage(topic, message, 0);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {

            }
            SerialPortRecvTimerS0_1.Start();
        }

        public string ToHexs(byte[] data)
        {
            if (data.Length <= 0) return "";
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                sb.Append(data[i].ToString("X2"));
            }
            string str = sb.ToString();
            return str;
        }

        public void QueryElevatorStatus()
        {
            try
            {
                Task task = new Task(async () =>
                {
                    if (cmd_token.IsCancellationRequested)
                    {
                        return;
                    }
                    cmd_resetEvent.WaitOne();
                    //do something
                    DateTime startTime = DateTime.Now;
                    string cmd = GetCmd();
                    while (true)
                    {
                        try
                        {
                            TimeSpan secondSpan = new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);
                            if (secondSpan.TotalMilliseconds > PZInterval)
                            {
                                if (string.IsNullOrEmpty(cmd))
                                {
                                    cmd = GetCmd();
                                }
                                if (reset)
                                {
                                    cmd = GetCmd();
                                    reset = false;
                                }
                                QueryttyS1_Status(cmd);
                                startTime = DateTime.Now;
                            }
                        }
                        catch
                        {

                        }
                    }
                }, cmd_token);
                task.Start();
            }
            catch (Exception e)
            {
            }
        }

        public string GetCmd()
        {
            try
            {
                byte[] cmd_buffer = new byte[6];
                cmd_buffer[0] = (byte)(liftid & 0xff);
                add_buffer = toBytes(add_start - 1);
                count_buffer = toBytes(add_count);

                cmd_buffer[1] = 0x03;
                cmd_buffer[2] = add_buffer[1];
                cmd_buffer[3] = add_buffer[0];
                cmd_buffer[4] = count_buffer[1];
                cmd_buffer[5] = count_buffer[0];
                byte[] crc = Crc18(cmd_buffer);
                byte[] data_crc = new byte[2];
                data_crc[0] = crc[1];
                data_crc[1] = crc[0];
                string cmd = ToHexStrFromByte(cmd_buffer) + " " + ToHexStrFromByte(data_crc);
                return cmd;
            }
            catch (Exception ex)
            {
                LogHelper.Log(ex.ToString());
                return "";
            }
        }

        public static byte[] toBytes(int number)
        {
            byte[] bytes = new byte[2];
            bytes[0] = (byte)number;
            bytes[1] = (byte)(number >> 8);
            return bytes;
        }

        public void QueryttyS1_Status(string cmd)
        {
            try
            {
                if (!string.IsNullOrEmpty(cmd))
                {
                    ttyS1SendMsg(cmd);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Log(ex.ToString());
                return;
            }
        }

        public bool ttyS1SendMsg(string data)
        {
            try
            {
                myS0SerialPort_1.Write(ToBytesSendDate(data), 0, ToBytesSendDate(data).Length);
                logPrint.Log("发送命令：" + data);
                return true;
            }
            catch (Exception e)
            {
                logPrint.Log("命令发送异常" + data + e.ToString());
                return false;
            }
        }

        public byte[] ToBytesSendDate(string str)
        {
            List<byte> result = new List<byte>();
            if (str.Contains(" "))
            {
                string[] strs = str.Trim().Split(' ');
                foreach (string s in strs)
                {
                    result.Add(Convert.ToByte(s.Trim(), 16));
                }
            }
            else
            {
                result.Add(Convert.ToByte(str, 16));
            }
            return result.ToArray();
        }

        #region CRC校验
        public byte[] Crc18(byte[] buffer, int start = 0, int len = 0)
        {
            if (buffer == null || buffer.Length == 0) return null;
            if (start < 0) return null;
            if (len == 0) len = buffer.Length - start;
            int length = start + len;
            if (length > buffer.Length) return null;
            ushort crc = 0xFFFF;// Initial value
            for (int i = start; i < length; i++)
            {
                crc ^= buffer[i];
                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 1) > 0)
                        crc = (ushort)((crc >> 1) ^ 0xA001);// 0xA001 = reverse 0x8005
                    else
                        crc = (ushort)(crc >> 1);
                }
            }
            byte[] ret = BitConverter.GetBytes(crc);
            Array.Reverse(ret);
            return ret;
        }
        #endregion

        public static string ToHexStrFromByte(byte[] byteDatas)
        {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < byteDatas.Length; i++)
            {
                builder.Append(string.Format("{0:X2} ", byteDatas[i]));
            }
            return builder.ToString().Trim();

        }
    }
}
